package com.sangmo.fts.training.service;

import com.sangmo.boot.framework.cache.meta.Cache;
import com.sangmo.boot.framework.common.errors.ServiceException;
import com.sangmo.boot.framework.common.errors.StandardErrors;
import com.sangmo.boot.framework.common.session.UserSession;
import com.sangmo.boot.framework.data.filter.PageResult;
import com.sangmo.boot.framework.data.filter.PageSearch;
import com.sangmo.boot.framework.data.filter.ValueFilter;
import com.sangmo.boot.framework.plugin.dataview.crud.service.DelegateViewService;
import com.sangmo.boot.framework.plugin.dataview.crud.service.ViewService;
import com.sangmo.fts.training.build.TrainingBuilder;
import com.sangmo.fts.training.interfaces.TrainingEnterRequest;
import com.sangmo.fts.training.model.Team;
import com.sangmo.fts.training.model.Teammate;
import com.sangmo.fts.training.model.Training;
import com.sangmo.fts.training.service.TrainingEntityService;
import com.sangmo.fts.training.vo.TrainingRightsView;
import com.sangmo.fts.user.service.PermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;


@Service
@Transactional(rollbackFor = Exception.class)
public class TrainingService extends DelegateViewService<Training> {

    @Autowired
    private TrainingEntityService trainingEntityService;

    @Autowired
    private TeamService teamService;

    @Autowired
    private TeammateEntityService teammateEntityService;

    @Autowired
    private PermissionService permissionService;


    @Override
    protected ViewService<Training> getEntityService() {
        return trainingEntityService;
    }

    @Cache(300)
    public Training findOneCache(Long id) {
        return this.find(id);
    }

    @Override
    public PageResult search(PageSearch ps) {
//        ps.getFilters().add(new ValueFilter("createdBy", ValueFilter.OP_EQ, UserSession.getUserInterface().getId()));
        return super.search(ps);
    }

    public Training enter(TrainingEnterRequest request) {
        Long currentUserId = (Long) UserSession.getUserInterface().getId();
        if (Training.Modes.INDIVIDUAL.equals(request.getMode())) {
            Training training = trainingEntityService.findPendingTraining(request.getBusinessType(), request.getMode(),
                    currentUserId);
            if (null == training) {
                Training newTraining = TrainingBuilder.newInstance()
                        .setBusinessType(request.getBusinessType())
                        .setMode(request.getMode())
                        .build();
                training = this.save(newTraining);
            }
            return training;
        } else if (Training.Modes.TEAM.equals(request.getMode())) {
            teamService.checkTeamValid(request.getTeamId(), currentUserId);
            Training training = trainingEntityService.findTeamTraining(request.getTeamId(), request.getBusinessType());
            if (null == training) {
                Training newTraining = TrainingBuilder.newInstance()
                        .setTeamId(request.getTeamId())
                        .setBusinessType(request.getBusinessType())
                        .setMode(request.getMode())
                        .build();
                training = this.save(newTraining);
            }

            return training;
        }
        return null;
    }

    @Cache(value = 300, parametrizedClass = List.class, parameterClass = Long.class)
    public List<Long> findTeammateUserIdsByTeamId(Long teamId) {
        List<Teammate> teammateList = teammateEntityService.findByTeamId(teamId);
        List<Long> userIds = teammateList.stream().map(Teammate::getUserId).collect(Collectors.toList());
        return userIds;
    }

    @Cache(300)
    public Set<String> findTeammateRolesByTeamIdAndUserId(Long teamId, Long userId) {
        Teammate teammate = teammateEntityService.findByTeamIdAndUserId(teamId, userId);
        return teammate == null?new HashSet<>() : new HashSet<>(Arrays.asList(teammate.getRole().split(",")));
    }

    public Training findByExaminationIdAndUserId(Long examinationId, Long userId) {
        return trainingEntityService.findByExaminationIdAndUserId(examinationId, userId);
    }

    public Training findByExaminationAndTeamId(Long examinationId, Long teamId) {
        return trainingEntityService.findByExaminationIdAndTeamId(examinationId, teamId);
    }

    public Collection<Training> findByExaminationId(Long examinationId) {
        return trainingEntityService.findByExaminationId(examinationId);
    }

    public void checkAccess(Training training, Long userId) {
        if (Training.Modes.INDIVIDUAL.equals(training.getMode())) {
            boolean canAccess = userId.equals(training.getCreatedBy())? true : false;
            if (!canAccess) {
                throw new ServiceException(StandardErrors.CLIENT_ERROR.getStatus(), "error.training.access.disabled");
            }
        } else {
            List<Long> userIds = this.findTeammateUserIdsByTeamId(training.getTeamId());
            boolean canAccess = userIds.contains(userId) ? true : false;
            if (!canAccess) {
                throw new ServiceException(StandardErrors.CLIENT_ERROR.getStatus(), "error.training.access.disabled");
            }
        }
    }

    public void checkSubmitAccess(Training training, Long userId) {
        if (null != training.getEndTime()) {
            throw new ServiceException(StandardErrors.CLIENT_ERROR.getStatus(), "error.training.duration.notvalid");
        }
        //团队实训队长才能提交实训
        if (Training.Modes.TEAM.equals(training.getMode())) {
            Team team = teamService.get(training.getTeamId());
            if (!userId.equals(team.getCreatedBy())) {
                throw new ServiceException(StandardErrors.CLIENT_ERROR.getStatus(), "error.training.submit.notallowed");
            }
        } else if (Training.Modes.INDIVIDUAL.equals(training.getMode())) {
            if (!userId.equals(training.getCreatedBy())) {
                throw new ServiceException(StandardErrors.CLIENT_ERROR.getStatus(), "error.training.access.disabled");
            }
        }
    }

    public TrainingRightsView rights(Long id) {
        Training training = this.get(id);
        Long currentUserId = (Long) UserSession.getUserInterface().getId();

        TrainingRightsView rightsView = new TrainingRightsView();
        //角色
        if (Training.Modes.INDIVIDUAL.equals(training.getMode())) {
            List<String> roles = Teammate.businessTypeMapRoles.get(training.getBusinessType());
            rightsView.setRoles(new HashSet<>(roles));

            Collection<String> perms = permissionService.findUriByApplication(training.getBusinessType());
            rightsView.setPermissions(perms);

        } else {
            Set<String> roles = this.findTeammateRolesByTeamIdAndUserId(training.getTeamId(), currentUserId);
            rightsView.setRoles(roles);

            Collection<String> perms = permissionService.findUriByApplicationAndRoles(training.getBusinessType(), roles);
            rightsView.setPermissions(perms);
        }

        return rightsView;
    }

}
